#include "IonDev.h"

namespace android::wmark
{
    int IonDev::ionExportBufferFd(struct ion_buffer_info *ion_info)
    {
        int i, ret, ionfd, buffer_fd;
        unsigned int heap_id;
        unsigned long maplen;
        unsigned char *map_buffer;
        struct ion_allocation_data alloc_data;
        struct ion_heap_query query;
        struct ion_heap_data heap_data[MAX_HEAP_COUNT];

        if (!ion_info)
        {
            fprintf(stderr, "<%s>: Invalid ion info\n", __func__);
            ALOGE("%s %d : Invalid ion info\n", __func__, __LINE__);
            return -1;
        }

        /* Create an ION client */
        ionfd = getFd();

        memset(&query, 0, sizeof(query));
        query.cnt = MAX_HEAP_COUNT;
        query.heaps = (unsigned long int)&heap_data[0];
        /* Query ION heap_id_mask from ION heap */
        ret = ioctl(ionfd, ION_IOC_HEAP_QUERY, &query);
        if (ret < 0)
        {
            fprintf(stderr, "<%s>: Failed: ION_IOC_HEAP_QUERY: %s\n",
                    __func__, strerror(errno));
            ALOGE("%s %d : Failed: ION_IOC_HEAP_QUERY: %s\n", __func__, __LINE__, strerror(errno));
            goto err_query;
        }

        heap_id = MAX_HEAP_COUNT + 1;
        for (i = 0; i < query.cnt; i++)
        {
            ALOGD("supported type id = %d ion_info->heap_type=%d\n", heap_data[i].type, ion_info->heap_type);
            if (heap_data[i].type == ion_info->heap_type)
            {
                heap_id = heap_data[i].heap_id;
                break;
            }
        }
        ALOGD("heap_id = %d, MAX_HEAP_COUNT=%d\n", heap_id, MAX_HEAP_COUNT);
        if (heap_id > MAX_HEAP_COUNT)
        {
            fprintf(stderr, "<%s>: ERROR: heap type does not exists\n",
                    __func__);
            ALOGE("%s ERROR: heap type does not exists\n", __func__);
            goto err_heap;
        }

        alloc_data.len = ion_info->heap_size;
        alloc_data.heap_id_mask = 1 << heap_id;
        alloc_data.flags = ion_info->flag_type;
        ALOGD("heap_id=%d flags=%d\n", heap_id, ion_info->flag_type);

        /* Allocate memory for this ION client as per heap_type */
        ret = ioctl(ionfd, ION_IOC_ALLOC, &alloc_data);
        if (ret < 0)
        {
            fprintf(stderr, "<%s>: Failed: ION_IOC_ALLOC: %s\n",
                    __func__, strerror(errno));
            ALOGE("%s %d : Failed: ION_IOC_ALLOC: %s\n", __func__, __LINE__, strerror(errno));
            goto err_alloc;
        }

        /* This will return a valid buffer fd */
        buffer_fd = alloc_data.fd;
        maplen = alloc_data.len;

        if (buffer_fd < 0 || maplen <= 0)
        {
            fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n",
                    __func__, buffer_fd, maplen);
            ALOGE("%s %d : Invalid map data, fd: %d, len: %ld\n", __func__, __LINE__, buffer_fd, maplen);
            goto err_fd_data;
        }

        map_buffer = NULL;
        ALOGD("%s %d map_buffer=%p buffer_fd=%d", __func__, __LINE__, map_buffer, buffer_fd);
        /* Create memory mapped buffer for the buffer fd */
        map_buffer = (unsigned char *)mmap(NULL, maplen, PROT_READ | PROT_WRITE,
                                           MAP_SHARED, buffer_fd, 0);
        ALOGD("%s %d map_buffer=%p buffer_fd=%d", __func__, __LINE__, map_buffer, buffer_fd);
        if (map_buffer == MAP_FAILED)
        {
            fprintf(stderr, "<%s>: Failed: mmap: %s\n",
                    __func__, strerror(errno));
            ALOGE("%s %d : Failed: mmap: %s\n", __func__, __LINE__, strerror(errno));
            goto err_mmap;
        }

        ion_info->ionfd = ionfd;
        ion_info->buffd = buffer_fd;
        ion_info->buffer = map_buffer;
        ion_info->buflen = maplen;

        ALOGD("%s %d : ionfd=%d buffd=%d buffer=%p buflen=%lu", __func__, __LINE__,
             ion_info->ionfd, ion_info->buffd, ion_info->buffer, ion_info->buflen);
        return 0;

        munmap(map_buffer, maplen);

    err_fd_data:
    err_mmap:
        /* in case of error: close the buffer fd */
        if (buffer_fd)
            close(buffer_fd);

    err_query:
    err_heap:
    err_alloc:
        /* In case of error: close the ion client fd */
        if (ionfd)
            close(ionfd);

        return -1;
    }

    int IonDev::ionImportBufferFd(struct ion_buffer_info *ion_info)
    {
        int buffd;
        unsigned char *map_buf;
        unsigned long map_len;

        if (!ion_info)
        {
            fprintf(stderr, "<%s>: Invalid ion info\n", __func__);
            return -1;
        }

        map_len = ion_info->buflen;
        buffd = ion_info->buffd;

        if (buffd < 0 || map_len <= 0)
        {
            fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n",
                    __func__, buffd, map_len);
            goto err_buffd;
        }

        map_buf = (unsigned char *)mmap(NULL, map_len, PROT_READ | PROT_WRITE,
                                        MAP_SHARED, buffd, 0);
        if (map_buf == MAP_FAILED)
        {
            printf("<%s>: Failed - mmap fd=%d len=%d: %s\n",
                   __func__, buffd, map_len, strerror(errno));
            goto err_mmap;
        }

        ion_info->buffer = map_buf;
        ion_info->buflen = map_len;

        return 0;

    err_mmap:
        if (buffd)
            close(buffd);

    err_buffd:
        return -1;
    }

    void IonDev::ionCloseBufferFd(struct ion_buffer_info *ion_info)
    {
        if (ion_info)
        {
            if (ion_info->buffer) {
                /* unmap the buffer properly in the end */
                munmap(ion_info->buffer, ion_info->buflen);
                ion_info->buffer = NULL;
                ALOGD("%s %d unmap.", __func__, __LINE__);
            }
            
            /* close the buffer fd */
            if (ion_info->buffd > 0) {
                ALOGD("%s %d close(%d).", __func__, __LINE__, ion_info->buffd);
                close(ion_info->buffd);
                ion_info->buffd = -1;
            }

        }
    }

    int IonDev::createBuf(struct ion_buffer_info &info, unsigned long len)
    {
        int opt, ret, status, heapid;
        int client_fd, shared_fd;
        unsigned char *map_buf;
        unsigned long map_len, heap_type, heap_size, flags;

        flags = 0;
        heap_size = len;
        heap_type = ION_HEAP_TYPE_SYSTEM;

        ALOGI("heap_type: %ld, heap_size: %ld\n", heap_type, heap_size);
        info.heap_type = heap_type;
        info.heap_size = heap_size;
        info.flag_type = flags;

        
        ret = ionExportBufferFd(&info);
        if (ret < 0)
        {
            fprintf(stderr, "FAILED: ion_get_buffer_fd\n");
            ALOGE("%s %d : FAILED: ion_get_buffer_fd\n", __func__, __LINE__);
            goto err_export;
        }
        client_fd = info.ionfd;
        shared_fd = info.buffd;
        map_buf = info.buffer;
        map_len = info.buflen;
        ALOGI("ionDevFd=%d dmaFd=%d map_buf=%p map_len=%d\n",
            client_fd, shared_fd, map_buf, map_len);

        return 0;
    err_export:
        ALOGE("%s %d fail\n", __func__, __LINE__);
        return -1;
    }
}